# -*- coding: utf-8 -*-
import operator
from formencode.validators import FancyValidator, Invalid

__all__ = ["CNPJ"]

class CNPJ(FancyValidator):
    u"""
    Esta classe é responsável por validar um número de CNPJ, dentre outras
    coisas verificando se os dígitos de controle e os números do documento são
    compatíveis e verificar se a quantidade de dígitos informada é a correta.

    A utilização dá-se como para outros validadores do FormEncode, o que permite
    que este validador seja usado em conjunto com outros não providos aqui ou
    então em regras mais complexas de validação.


    Parâmetros:

    value    -> valor que sofrerá a ação
    messages -> dicionário contendo as mensagens de erro
    format_output -> informa se o valor retornado deve ou não ser formatado com
                     "." e "-" para a exibição
    strict   -> se verdadeiro, falha para números conhecidamente inválidos
    allow_invalid_format -> informa se números como "00.000.000//0000--00" devem
                            ou não ser aceitos
    allow_missing_format -> informa se números como "0000000000000" devem ou não
                            ser aceitos
    """

    format_output = True
    strict = True
    allow_invalid_format = True
    allow_missing_format = True
    messages = {
        'empty': 'Por favor entre com um CNPJ',
        'invalid': 'CNPJ invalido',
        'size': 'Numero de digitos incorreto',
        'format': 'Formatacao do numero incorreta',
        }

    def _to_python(self, value, state):
        def __checa_formato(value):
            if (value[2], value[6], value[10], value[15]) == ('.', '.', '/', '-'):
                return value[:2] + value[3:6] + value[7:10] + value[11:15] + value[16:]
            raise Invalid(self.message('format', state), value, state)

        if self.allow_invalid_format:
            value = value.replace('.', '').replace('-', '').replace('/', '')
            return value
        elif self.allow_missing_format:
            if len(value) == 14:
                return value
            if len(value) == 18:
                return __checa_formato(value)
        else: # not missing and not invalid
            if len(value) == 18:
                return __checa_formato(value)

        raise Invalid(self.message('format', state), value, state)


    def validate_python(self, value, state):
        if not len(value) == 14:
            raise Invalid(self.message('size', state), value, state)

        if not value.isdigit():
            raise Invalid(self.message('invalid', state), value, state)

        if self.strict:
            numbers = [6*(u"%.2d" % (x*10)) + u"%.2d" % (x*11)
                       for x in range(10)]
            if value in numbers:
                raise Invalid(self.message('invalid', state), value, state)

        resto = reduce(operator.add, [((11-i) % 8 + 2) * int(v)
                                      for i, v in enumerate(value[:12])]) % 11
        digito1 = (resto > 1) and (11 - resto) or 0
        resto = reduce(operator.add, [((12-i) % 8 + 2) * int(v)
                                      for i, v in enumerate(value[:13])]) % 11
        digito2 = (resto > 1) and (11 - resto) or 0

        if ([digito1, digito2] != [int(value[12]), int(value[13])]):
            raise Invalid(self.message('invalid', state), value, state)

    def _from_python(self, value, state):
        if len(value) == 14 and self.format_output:
            return "%s.%s.%s/%s-%s" % (value[0:2], value[2:5], value[5:8],
                                       value[8:12], value[12:14])
        else:
            # Assume-se que o valor vem de uma fonte "correta" e pode chegar
            # ao usuário desta forma.
            return value
